package com.example.lotterysystem.service.mq;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.lotterysystem.common.config.ExecutorConfig;
import com.example.lotterysystem.common.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.common.enums.ActivityStatusEnum;
import com.example.lotterysystem.common.enums.ActivityUserStatusEnum;
import com.example.lotterysystem.common.enums.PrizeGradeEnum;
import com.example.lotterysystem.common.exception.GlobalException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.MailUtil;
import com.example.lotterysystem.common.utils.SMSUtil;
import com.example.lotterysystem.controller.param.LotteryParam;
import com.example.lotterysystem.dao.dataobject.ActivityPrize;
import com.example.lotterysystem.dao.dataobject.WinningRecord;
import com.example.lotterysystem.mapper.ActivityPrizeMapper;
import com.example.lotterysystem.mapper.WinningRecordMapper;
import com.example.lotterysystem.service.LotteryService;
import com.example.lotterysystem.service.activitystatus.ActivityStatusManager;
import com.example.lotterysystem.service.dto.TransferActivityStatusDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;
@Slf4j
@Component
@RabbitListener(queues = QUEUE_NAME)
public class MqReceiver {
    @Autowired
    private LotteryService lotteryService;

    @Autowired
    private ActivityStatusManager activityStatusManager;

    @Autowired
    private MailUtil mailUtil;

    @Autowired
    private SMSUtil smsUtil;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    @Autowired
    private ExecutorConfig executorConfig;
    @RabbitHandler
    public void process(Map<String, String> message){
        // 接收到消息
        log.info("接收到的消息: {}", JacksonUtil.writeValueAsString(message));
        String param = message.get("messageData");
        LotteryParam lotteryParam = JacksonUtil.readValue(param, LotteryParam.class);
        // 处理抽奖流程
        try {
            // 1. 校验抽奖请求是否有效
            if(!lotteryService.checkLotteryParam(lotteryParam)){
                return;
            }

            // 2. 进行状态扭转
            statusTransfer(lotteryParam);

            // 3. 保存中奖名单
            List<WinningRecord> winningRecords =
                    lotteryService.saveLuckyRecords(lotteryParam);

            // 4. 通知中奖者
            syncExecute(winningRecords);

            // 异常情况处理
        } catch (GlobalException e){
            // 事务回滚
            rollback(lotteryParam);
            log.error("消费 mq 消息异常! {}, {} ", e.getCode(), e.getMessage() + e.getErrorCause(), e);
            throw e;
        } catch (Exception e){
            log.error("消费 mq 消息未知异常!", e);
            throw e;
        }
    }

    private void rollback(LotteryParam param){
        // 回滚状态
        ActivityPrize activityPrize = activityPrizeMapper.selectOne(
                new QueryWrapper<ActivityPrize>()
                        .eq("activity_id", param.getActivityId())
                        .eq("prize_id", param.getPrizeId()));
        if(!activityPrize.getStatus()
                .equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            return;
        }
        TransferActivityStatusDTO statusDTO = new TransferActivityStatusDTO();
        statusDTO.setActivityId(param.getActivityId());
        statusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        statusDTO.setPrizeId(param.getPrizeId());
        statusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.INIT);
        statusDTO.setUserIds(param.getWinnerList().stream()
                .map(LotteryParam.Lucky::getUserId).toList());
        statusDTO.setTargetUserStatus(ActivityUserStatusEnum.INIT);
        activityStatusManager.rollbackHandleEvent(statusDTO);

        // 回滚中奖名单表
        QueryWrapper<WinningRecord> wrapper =
                new QueryWrapper<WinningRecord>()
                .eq("activity_id", param.getActivityId())
                .eq("prize_id", param.getPrizeId());
        Long count = winningRecordMapper.selectCount(wrapper);
        if(count == 0){
            return;
        }
        lotteryService.deleteRecords(param.getActivityId(), param.getPrizeId());
    }

    /**
     * 并发处理抽奖通知
     * @param winningRecords
     */
    private void syncExecute(List<WinningRecord> winningRecords) {
        if(CollectionUtils.isEmpty(winningRecords)){
            log.warn("发送中奖通知失败，中奖列表为空");
            return;
        }
        ThreadPoolTaskExecutor threadPoolTaskExecutor = executorConfig.asyncServiceExecutor();
        threadPoolTaskExecutor.execute(() -> sendMessage(winningRecords));
        threadPoolTaskExecutor.execute(() -> sendMail(winningRecords));
    }

    private void sendMail(List<WinningRecord> winningRecords) {
        for (WinningRecord winningRecord : winningRecords) {
            String context = "尊敬的用户 " + winningRecord.getWinnerName() +
                    " 您好，恭喜您在 " + winningRecord.getActivityName() +
                    " 活动中获得 " + PrizeGradeEnum.forName(winningRecord.getPrizeTier()).getValue() +
                    " 奖品为 " + winningRecord.getPrizeName() +
                    "，获奖时间为 " + DateUtil.formatTime(winningRecord.getWinningTime()) +
                    " 请尽快领取您的奖品";
            mailUtil.sendSampleMail(winningRecord.getWinnerEmail(), "中奖通知", context);
        }
    }

    private void sendMessage(List<WinningRecord> winningRecords){
        for (WinningRecord winningRecord : winningRecords) {
            Map<String, String> map = new HashMap<>();
            map.put("name", winningRecord.getWinnerName());
            map.put("activityName", winningRecord.getActivityName());
            map.put("prizeTiers", winningRecord.getPrizeTier());
            map.put("prizeName", winningRecord.getPrizeName());
            map.put("winningTime", DateUtil.formatTime(winningRecord.getWinningTime()));
            smsUtil.sendMessage("SMS_473800200",
                    winningRecord.getWinnerPhoneNumber(),
                    JacksonUtil.writeValueAsString(map));
        }
    }
    private void statusTransfer(LotteryParam param) {
        // 活动状态 RUNNING -> COMPLETED 全部奖品抽取完成之后才会改变状态
        // 奖品状态 INIT -> COMPLETED
        // 人员状态 INIT -> COMPLETED
        // 更新缓存
        // 责任链模式 + 策略模式
        TransferActivityStatusDTO statusDTO = new TransferActivityStatusDTO();
        statusDTO.setActivityId(param.getActivityId());
        statusDTO.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        statusDTO.setPrizeId(param.getPrizeId());
        statusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.COMPLETED);
        statusDTO.setUserIds(param.getWinnerList().stream()
                .map(LotteryParam.Lucky::getUserId).toList());
        statusDTO.setTargetUserStatus(ActivityUserStatusEnum.COMPLETED);

        activityStatusManager.handlerEvent(statusDTO);
    }
}
